/**
 * \file AGénérationGrillePleine.c
 * \author Catalin BLAJ
 * \date 30 Octobre 2013
 *
 * Fichier source AGenerationGrillePleine.c pour le programme du Sudoku
 */

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <assert.h>

#include "AGenerationGrille.h"
#include "AUniciteGrille.h"
#include "AResolutionLogique.h"
#include "SGrille.h"


/**
 * \brief Fonction qui permute les lignes
 * \param indice1: Le numero de ligne à échanger avec indice2
 * \param indice2: Le numero de ligne à échanger avec indice1
 * \param grille : La grille à permuter
 *
 *  Permuter les lignes
 */
static void GG_permutationLigne(int indice1, int indice2, SGrille* grille)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned short nombreUnites = type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);

    /* On permute valeur par valeur */
    short valeurTemp;
    for (unsigned int i=0; i<nombreUnites; i++)
    {
        valeurTemp = SC_getValeur(SU_getCase(SG_getLigne(grille, indice1), i));
        SC_setValeur(SU_getCase(SG_getLigne(grille, indice1), i), SC_getValeur(SU_getCase(SG_getLigne(grille, indice2), i)));
        SC_setValeur(SU_getCase(SG_getLigne(grille, indice2), i), valeurTemp);
    }
}


/**
 * \brief Fonction qui permute les colonnes
 * \param indice1: Le numero de colonne à échanger avec indice2
 * \param indice2: Le numero de colonne à échanger avec indice1
 * \param grille : La grille à permuter
 *
 *  Permuter les colonnes
 */
static void GG_permutationColonne(int indice1, int indice2, SGrille* grille)
{
    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned short nombreUnites = type*type;

    /* Vérification des paramètres */
    assert(grille != NULL);

    /* On permute valeur par valeur */
    short valeurTemp;
    for (unsigned int i=0; i<nombreUnites; i++)
    {
        valeurTemp = SC_getValeur(SU_getCase(SG_getLigne(grille, indice1), i));
        SC_setValeur(SU_getCase(SG_getLigne(grille, indice1), i), SC_getValeur(SU_getCase(SG_getLigne(grille, indice2), i)));
        SC_setValeur(SU_getCase(SG_getLigne(grille, indice2), i), valeurTemp);
    }
}


/**
 * \brief Fonction qui permute aléatoirement les lignes
 * \param min : Le minimum qui fixe le numero de ligne au min
 * \param max : Le maximum qui fixe le numero de ligne au max
 * \param grille : La grille à permuter
 *
 *  Permuter aléatoirement les lignes
 */
static void GG_permutationLigneHasard(int min, int max, SGrille* grille)
{
    /* Vérification des paramètres */
    assert(grille != NULL);

	int indice1; /* Un indice au hasard entre min et max */
	int indice2; /* Un indice au hasard entre min et max différent de indice1 */

	srand((unsigned int)time(NULL)); /* Pour la fonction rand(); */

    /* On permutera au hasard 10 fois */
    for (int i=0; i<10; i++)
    {
        indice1 = rand()%(max-min+1)+min;
        do
            indice2 = rand()%(max-min+1)+min;
        while (indice1 == indice2);

        GG_permutationLigne(indice1, indice2, grille);
    }
}


/**
 * \brief Fonction qui permute aléatoirement les colonnes
 * \param min : Le minimum qui fixe le numéro de colonne au min
 * \param max : Le maximum qui fixe le numéro de colonne au max
 * \param grille : La grille à permuter
 *
 *  Permuter aléatoirement les colonnes
 */
static void GG_permutationColonneHasard(int min, int max, SGrille* grille)
{
	int indice1; /* Un indice au hasard entre min et max */
	int indice2; /* Un indice au hasard entre min et max différent de indice1 */

	srand((unsigned int)time(NULL)); /* Pour la fonction rand(); */

    /* On permutera au hasard 10 fois */
    for (int i=0; i<10; i++)
    {
        indice1 = rand()%(max-min+1)+min;
        do
            indice2 = rand()%(max-min+1)+min;
        while (indice1 == indice2);

        GG_permutationColonne(indice1, indice2, grille);
    }
}


/**
 * \brief Fonction qui permute les piles
 * \param grille : La grille à permuter
 *
 *  Permuter aléatoirement les piles
 */
static void GG_permutationBande(SGrille* grille)
{
    srand (time (NULL));
    unsigned char type = SG_getType(grille);

	int n; /* Indice le plus bas d'une des 3 bandes */
	int n1; /* Indice le plus bas d'une des 2 premières bandes */
	int f;
	int f1;

    /* On va permutter 10 fois */
    for(int i=0; i<10; i++)
    {
        do
        {
            n = type*(rand()%type);
            n1= type*(rand()%type);
        }
        while(n != n1);


        for (int i=0; i<type; i++)
        {
            /*On échange la première ligne de deux bandes */
            f = n+i;
            f1 = n1+i;
            GG_permutationLigne(f, f1, grille);
        }
    }
}


/**
 * \brief Fonction qui permute les bandes
 * \param grille : La grille à permuter
 *
 *  Permuter aléatoirement les bandes
 */
static void GG_permutationPile(SGrille* grille)
{
    srand (time (NULL));
    unsigned char type = SG_getType(grille);

	int n; /* Indice le plus bas d'une des 3 bandes */
	int n1; /* Indice le plus bas d'une des 2 premières bandes */
	int f;
	int f1;

    /* On va permutter 10 fois */
    for(int i=0; i<10; i++)
    {
        do
        {
            n = type*(rand()%type);
            n1= type*(rand()%type);
        }
        while(n != n1);

        for (int i=0; i<type; i++)
        {
            /*On échange la première ligne de deux bandes */
            f = n+i;
            f1 = n1+i;
            GG_permutationColonne(f, f1, grille);
        }
    }
}


void GG_genererSGrille(SGrille* grille)
{
    assert(grille != NULL);

    /* Variables de convenance */
	unsigned char type = SG_getType(grille);

	int max; /* Indice maximum pour le mélange des lignes/colonnes */
	int min; /* Indice minimum pour le mélange des lignes/colonnes */

    /* Si la grille est de 16x16 */
    if (type == 4)
    {
        /* On crée la grille à partir d'un tableau 16x16 */
        int grilleTab[16][16]=
        {
            {11, 7, 9, 5, 15, 8, 10, 0, 13, 12, 1, 6, 2, 3, 4, 14},
            {8, 2, 1, 12, 4, 14, 9, 6, 7, 3, 10, 0, 11, 5, 13, 15},
            {3, 6, 0, 15, 11, 2, 1, 13, 4, 5, 9, 14, 7, 10, 12, 8},
            {13, 14, 10, 4, 5, 12, 3, 7, 8, 15, 2, 11, 9, 1, 6, 0},
            {4, 5, 8, 14, 7, 9, 15, 12, 6, 0, 11, 2, 3, 13, 10, 1},
            {12, 11, 6, 1, 8, 0, 2, 3, 15, 10, 13, 4, 5, 14, 7, 9},
            {10, 3, 15, 13, 14, 6, 5, 11, 1, 9, 7, 8, 4, 2, 0, 12},
            {0, 9, 2, 7, 13, 1, 4, 10, 12, 14, 3, 5, 6, 15, 8, 11},
            {15, 4, 7, 9, 0, 5, 8, 1, 14, 11, 6, 3, 13, 12, 2, 10},
            {5, 0, 14, 8, 6, 10, 11, 4, 2, 1, 12, 13, 15, 7, 9, 3},
            {1, 10, 13, 3, 12, 15, 14, 2, 5, 7, 8, 9, 0, 4, 11, 6},
            {6, 12, 11, 2, 3, 13, 7, 9, 0, 4, 15, 10, 14, 8, 1, 5},
            {14, 13, 4, 0, 2, 11, 12, 8, 10, 6, 5, 15, 1, 9, 3, 7},
            {9, 8, 5, 6, 1, 7, 0, 15, 3, 13, 4, 12, 10, 11, 14, 2},
            {7, 15, 12, 10, 9, 3, 13, 14, 11, 2, 0, 1, 8, 6, 5, 4},
            {2, 1, 3, 11, 10, 4, 6, 5, 9, 8, 14, 7, 12, 0, 15, 13}
        };

        SG_remplirGrilleAvecTableau16x16(grille, grilleTab);
    }

    /* Si la grille est de 9x9 */
    else if (type == 3)
    {
        /* On crée la grille à partir d'un tableau 9x9 */
        int grilleTab[9][9]=
        {
            {4, 0, 3, 6, 7, 2, 5, 1, 8},
            {7, 5, 8, 3, 1, 4, 6, 2, 0},
            {2, 1, 6, 8, 0, 5, 7, 4, 3},
            {8, 6, 1, 5, 2, 7, 3, 0, 4},
            {0, 3, 4, 1, 6, 8, 2, 7, 5},
            {5, 7, 2, 4, 3, 0, 1, 8, 6},
            {1, 4, 0, 7, 5, 6, 8, 3, 2},
            {6, 8, 7, 2, 4, 3, 0, 5, 1},
            {3, 2, 5, 0, 8, 1, 4, 6, 7}
        };

        SG_remplirGrilleAvecTableau9x9(grille, grilleTab);
    }

    /* Si la grille est de 4x4 */
    else
    {
        /* On crée la grille à partir d'un tableau 9x9 */
        int grilleTab[4][4]=
        {
            {1, 2, 0, 3},
            {0, 3, 1, 2},
            {3, 0, 2, 1},
            {2, 1, 3, 0}
        };

        SG_remplirGrilleAvecTableau4x4(grille, grilleTab);
    }

    /* On mélange les bandes/piles, les lignes dans chacune des bandes et colonnes dans chacune des piles au hasard */
    for (unsigned char i=0; i<type; i++)
    {
        min = i*type;
        max = min+type-1;
        GG_permutationLigneHasard(min, max, grille);
        GG_permutationColonneHasard(min, max, grille);
        GG_permutationPile(grille);
        GG_permutationBande(grille);
    }
}


void GG_retraitRevelesGrille(SGrille* grille, const SGrille* grilleSolution, Difficulte difficulte)
{
    assert(grille != NULL);
    assert(grilleSolution != NULL);

    /* Variables de convenance */
    unsigned char type = SG_getType(grille);
    unsigned int nombreCases = type*type*type*type;

    int nombreTrous = 0;
    int trousEffectues = 0;

    srand((unsigned int)time(NULL)); /* Pour la fonction rand(); */srand((unsigned int)time(NULL));

    /* On définit le nombre de cases à enlever en fonction de la difficulté et de N */
    switch (type)
    {
        /* Pour n=2, la difficulté importe peu. On fera 12 les 16 */
    	case 2:
    		nombreTrous = 10;
    		break;

        /* Pour n=3 :
                En facile : On fera 36 trous sur 81
                En moyen : On fera 54 trous sur 81
                En difficile : On fera 63 trous sur 81
        */
    	case 3:
    		switch (difficulte)
    		{
    			case facile:
    				nombreTrous = 36;
    				break;
    			case moyen:
    				nombreTrous = 45;
    				break;
                case difficile:
    				nombreTrous = 54;
    				break;
    			case suicidaire:
    				nombreTrous = 63;
    				break;
    		}
    		break;

        /* Pour n=4 :
                En facile : On fera 112 trous sur 256
                En moyen : On fera 176 trous sur 256
                En difficile : On fera 208 trous sur 256
        */
    	case 4:
    		switch (difficulte)
    		{
    			case facile:
    				nombreTrous = 112;
    				break;
    			case moyen:
    				nombreTrous = 144;
    				break;
                case difficile:
    				nombreTrous = 176;
    				break;
    			case suicidaire:
    				nombreTrous = 208;
    				break;
    		}
    		break;
    }

    /* Ceci est un compteur de tentatives échouées à enlever un révélé sans donner plusieurs solutions */
	unsigned compteur = 0;

    while(trousEffectues < nombreTrous)
    {
        /*On choisit une case aléatoire */
        unsigned indice = (unsigned) (rand()%(nombreCases));
        while (SC_getEtat(SG_getCase(grille, indice)) == vide) /*Si la case est vide, on choisit la suivante */
        {
            indice++;
            indice %= nombreCases; /* Au cas où on soit à la dernière case */
        }

        /* On retire ce révélé et on met à jour ses candidats */
        SC_setEtat(SG_getCase(grille, indice), vide);

        /* METHODE AVEC SOLUTION UNIQUE : On copie la grille, on la résout pour connaitre le nombre de solutions */
        SGrille* grilleCopie = SG_initialiserCopie(grille);
        /*int nombreSolutions = RA_resolution(grilleCopie);*/
        int nombreSolutions = UG_resolution(grilleCopie);
        SG_testament(grilleCopie);

        /* METHODE STANDARD : On troue la grille sans se soucier du nombre de solutions*/
        //int nombreSolutions = 1;

        /* Si après avoir retirer ce révélé, on a toujours une solution unique, on troue définitivement :
            - On passe la case correspondante de la grille solution à valide (pour pouvoir distinguer les cases trouées des cases présentes.
            - On retire les candidats de la grille
            - On passe la valeur à -1 (simple convention)
        */
        if (nombreSolutions == 1)
        {
            SC_setEtat(SG_getCase(grilleSolution, indice), valide);
            SC_setCandidats(SG_getCase(grille, indice), 0);
            SC_setValeur(SG_getCase(grille, indice), -1);
            trousEffectues++;
            compteur = 0; /* On réinitialise le compteur */
        }

        /* Sinon, la case garde son révélé */
        else
            SC_setEtat(SG_getCase(grille, indice), gele);

        /* On s'autorise 5 échecs (sinon le programme peut prendre du temps) */
        compteur++;
        if (compteur > 10)
            break;
    }
}
